﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using System.Windows.Forms;
using Kugar.Core.ExtMethod;

namespace Kugar.UI.WinForm
{
    public class LabelEx:Label
    {
        private float borderSize;
        private Color borderColor;
        private Pen drawBorderPen;

        private PointF point;
        private SizeF drawSize;
        private Pen drawPen;
        private GraphicsPath drawPath;
        private SolidBrush forecolorBrush;

        //private SolidBrush drawBorderBrush;


        public LabelEx():base()
        {
            this.borderSize = 1f;
            this.borderColor = Color.Black;
            this.drawPath = new GraphicsPath();

            this.forecolorBrush = new SolidBrush(this.ForeColor);
            //this.drawPen = new Pen(forecolorBrush, this.Font.);

            drawBorderPen=new Pen(new SolidBrush(borderColor),borderSize);

            base.BorderStyle = BorderStyle.None;
            
            this.Invalidate();
        }

        #region Public Properties
        /// <summary>
        ///   The border's thickness
        /// </summary>
        [Browsable(true)]
        [Category("Appearance")]
        [Description("The border's thickness")]
        [DefaultValue(1f)]
        public float BorderSize
        {
            get { return this.borderSize; }
            set
            {
                this.borderSize = value;
                if (value == 0)
                {
                    //If border size equals zero, disable the
                    // border by setting it as transparent
                    this.drawBorderPen.Color = Color.Transparent;
                }
                else
                {
                    this.drawBorderPen.Color =borderColor;
                    this.drawBorderPen.Width = value;
                    borderSize = value;
                }

                this.Refresh();

                //this.OnTextChanged(EventArgs.Empty);
            }
        }

        /// <summary>
        ///   The border color of this component
        /// </summary>
        [Browsable(true)]
        [Category("Appearance")]
        [DefaultValue(typeof(Color), "White")]
        [Description("The border color of this component")]
        public Color BorderColor
        {
            get { return this.borderColor; }
            set
            {
                this.borderColor = value;

                //if (this.BorderSize != 0)
                    this.drawBorderPen.Color = value;
                    this.Refresh();
                //this.Invalidate();
            }
        }
        #endregion

        private BorderStyle borderStyle=BorderStyle.None;
        public new BorderStyle BorderStyle
        {
            get
            {
                return borderStyle;
            }
            set
            {
                if (borderStyle!=value)
                {
                    borderStyle = value;
                    //base.BorderStyle=BorderStyle.None;

                    this.Refresh();

                    //this.Invalidate();

                    //var t = base.BorderStyle;
                }
            }
        }

        // Event Handling
        //-----------------------------------------------------

        #region Event Handling
        protected override void OnFontChanged(EventArgs e)
        {
            base.OnFontChanged(e);
            this.Invalidate();
        }

        protected override void OnTextAlignChanged(EventArgs e)
        {
            base.OnTextAlignChanged(e);
            this.Invalidate();
        }

        protected override void OnTextChanged(EventArgs e)
        {
            base.OnTextChanged(e);
        }

        protected override void OnForeColorChanged(EventArgs e)
        {
            this.forecolorBrush.Color = base.ForeColor;
            base.OnForeColorChanged(e);
            this.Invalidate();
        }
        #endregion

        private int _Radius = 20;  // 圆角弧度
        private GraphicsPath oPath = new GraphicsPath();

        /// <summary>圆角弧度(0为不要圆角)</summary>
        [Browsable(true)]
        [Description("圆角弧度(0为不要圆角)")]
        [DefaultValue(20)]
        public int RoundRadius
        {
            get
            {
                return _Radius;
            }
            set
            {
                if (value < 0) { _Radius = 0; }

                if (_Radius!=value)
                {
                    oPath.Dispose();

                    float thisWidth = this.Width;
                    float thisHeight = this.Height;

                    if (AutoSize)
                    {
                        thisWidth = drawSize.Width + 3;
                        thisHeight = drawSize.Height + 3;

                    }

                    oPath = getPath(new RectangleF(0, 0, thisWidth, thisHeight), value);

                    Region=new Region(oPath);

                    _Radius = value;

                    if (value>0)
                    {
                        borderStyle = BorderStyle.FixedSingle;
                    }

                    base.Refresh();
                }



                //else { _Radius = value; }
                
            }
        }

        private string _text;
        public override string Text
        {
            get
            {
                return _text;
            }
            set
            {
                _text = value;

                if (AutoSize)
                {
                    oPath.Dispose();

                    var g = this.CreateGraphics();

                    this.drawSize =g .MeasureString(this.Text, this.Font, new PointF(),StringFormat.GenericTypographic);
            

                    g.Dispose();

                    float thisWidth = drawSize.Width + 3;
                    float thisHeight = drawSize.Height + 3;

                    oPath = getPath(new RectangleF(0, 0, thisWidth, thisHeight), _Radius);

                    Region = new Region(oPath);


                }

                this.Refresh();
            }
        }

        //// 圆角代码
        //private GraphicsPath Round()
        //{
        //    // -----------------------------------------------------------------------------------------------
        //    // 已经是.net提供给我们的最容易的改窗体的属性了(以前要自己调API)
        //    var oPath = new GraphicsPath();
        //    int x = 0;
        //    int y = 0;
        //    int thisWidth = this.Width;
        //    int thisHeight = this.Height;
        //    int angle = _Radius;
        //    if (angle > 0)
        //    {
        //        Graphics g = CreateGraphics();
        //        oPath.AddArc(x, y, angle, angle, 180, 90);                                 // 左上角
        //        oPath.AddArc(thisWidth - angle, y, angle, angle, 270, 90);                 // 右上角
        //        oPath.AddArc(thisWidth - angle, thisHeight - angle, angle, angle, 0, 90);  // 右下角
        //        oPath.AddArc(x, thisHeight - angle, angle, angle, 90, 90);                 // 左下角
        //        oPath.CloseAllFigures();
        //        Region = new Region(oPath);

                
        //    }
        //    // -----------------------------------------------------------------------------------------------
        //    else
        //    {
        //        oPath.AddLine(x + angle, y, thisWidth - angle, y);                         // 顶端
        //        oPath.AddLine(thisWidth, y + angle, thisWidth, thisHeight - angle);        // 右边
        //        oPath.AddLine(thisWidth - angle, thisHeight, x + angle, thisHeight);       // 底边
        //        oPath.AddLine(x, y + angle, x, thisHeight - angle);                        // 左边
        //        oPath.CloseAllFigures();
        //        Region = new Region(oPath);
        //    }
        //}

        private GraphicsPath getPath(RectangleF rect, int radius)
        {
            var tempPath = new GraphicsPath();
            float x = rect.Left;
            float y = rect.Top;
            float thisWidth = rect.Width;// this.Width;
            float thisHeight = rect.Height;// this.Height;
            int angle = radius;
            if (angle > 0)
            {
                //Graphics g = CreateGraphics();
                tempPath.AddArc(x, y, angle, angle, 180, 90);                                 // 左上角
                tempPath.AddArc(thisWidth - angle, y, angle, angle, 270, 90);                 // 右上角
                tempPath.AddArc(thisWidth - angle, thisHeight - angle, angle, angle, 0, 90);  // 右下角
                tempPath.AddArc(x, thisHeight - angle, angle, angle, 90, 90);                 // 左下角
                tempPath.CloseAllFigures();
                //Region = new Region(tempPath);


            }
            // -----------------------------------------------------------------------------------------------
            else
            {
                tempPath.AddLine(x , y, thisWidth , y);                         // 顶端
                tempPath.AddLine(thisWidth, y , thisWidth, thisHeight);        // 右边
                tempPath.AddLine(thisWidth, thisHeight, x, thisHeight);       // 底边
                tempPath.AddLine(x, y, x, thisHeight );                        // 左边
                tempPath.CloseAllFigures();


                //tempPath.AddLine(x + angle, y, thisWidth - angle, y);                         // 顶端
                //tempPath.AddLine(thisWidth, y + angle, thisWidth, thisHeight - angle);        // 右边
                //tempPath.AddLine(thisWidth - angle, thisHeight, x + angle, thisHeight);       // 底边
                //tempPath.AddLine(x, y + angle, x, thisHeight - angle);                        // 左边
                //tempPath.CloseAllFigures();
                //Region = new Region(oPath);
            }

            return tempPath;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            // First let's check if we indeed have text to draw.
            //  if we have no text, then we have nothing to do.
            //if (this.Text.Length == 0)
            //    return;
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
            

            //base.OnPaint(e);

            // Secondly, let's begin setting the smoothing mode to AntiAlias, to
            // reduce image sharpening and compositing quality to HighQuality,
            // to improve our drawnings and produce a better looking image.


            // Next, we measure how much space our drawning will use on the control.
            //  this is important so we can determine the correct position for our text.
            this.drawSize = e.Graphics.MeasureString(this.Text, this.Font, new PointF(),
                        StringFormat.GenericTypographic);

            // Now, we can determine how we should align our text in the control
            //  area, both horizontally and vertically. If the control is set to auto
            //  size itself, then it should be automatically drawn to the standard position.

            var tempBorderWidth = borderSize;

            if (borderStyle== BorderStyle.None)
            {
                tempBorderWidth = 0;
            }



            if (this.AutoSize)
            {
                this.point.X = Math.Max(this.Padding.Left, this.ClientRectangle.Left) + tempBorderWidth;// left;// Math.Max(this.Padding.Left, this.ClientRectangle.Left) + tempBorderWidth;
                this.point.Y = Math.Max(this.Padding.Top, this.ClientRectangle.Top) + tempBorderWidth;// top;// Math.Max(this.Padding.Top, this.ClientRectangle.Top) + tempBorderWidth;

            }
            else
            {
                var left = Math.Max(this.Padding.Left, this.ClientRectangle.Left) + tempBorderWidth;
                var top = Math.Max(this.Padding.Top, this.ClientRectangle.Top) + tempBorderWidth;

                //right = this.Padding.Right - tempBorderWidth;
                //bottom = this.Padding.Bottom - tempBorderWidth;
                var offset = _Radius == 0 ? 0 : _Radius / 2;
                var width = this.ClientRectangle.Width - this.Padding.Right - left - tempBorderWidth;// this.Padding.Right - tempBorderWidth;
                var height = this.ClientRectangle.Height - this.Padding.Bottom - top - tempBorderWidth;// bottom - top;// this.Padding.Bottom - tempBorderWidth;

                // Text is Left-Aligned:
                if (this.TextAlign == ContentAlignment.TopLeft ||
                    this.TextAlign == ContentAlignment.MiddleLeft ||
                    this.TextAlign == ContentAlignment.BottomLeft)
                    this.point.X = left+offset;// this.Padding.Left;

                // Text is Center-Aligned
                else if (this.TextAlign == ContentAlignment.TopCenter ||
                    this.TextAlign == ContentAlignment.MiddleCenter ||
                    this.TextAlign == ContentAlignment.BottomCenter)
                    point.X = (width - this.drawSize.Width) / 2; //(this.ClientRectangle.Width - this.drawSize.Width) / 2;

                // Text is Right-Aligned
                else point.X = width - (this.Padding.Right + this.drawSize.Width)-offset;// this.ClientRectangle.Width - (this.Padding.Right + this.drawSize.Width);

                // Text is Top-Aligned
                if (this.TextAlign == ContentAlignment.TopLeft ||
                    this.TextAlign == ContentAlignment.TopCenter ||
                    this.TextAlign == ContentAlignment.TopRight)
                    point.Y = top;// Math.Max(this.Padding.Top, this.ClientRectangle.Top);// this.Padding.Top;

                // Text is Middle-Aligned
                else if (this.TextAlign == ContentAlignment.MiddleLeft ||
                    this.TextAlign == ContentAlignment.MiddleCenter ||
                    this.TextAlign == ContentAlignment.MiddleRight)
                    point.Y = (height - this.drawSize.Height) / 2;// (this.ClientRectangle.Height - this.drawSize.Height) / 2;

                // Text is Bottom-Aligned
                else point.Y = height - (this.Padding.Bottom + this.drawSize.Height)    ;// this.ClientRectangle.Height - (this.Padding.Bottom + this.drawSize.Height);
            }

            // Now we can draw our text to a graphics path.
            //  
            //   PS: this is a tricky part: AddString() expects float emSize in pixel, 
            //   but Font.Size measures it as points.
            //   So, we need to convert between points and pixels, which in
            //   turn requires detailed knowledge of the DPI of the device we are drawing on. 
            //
            //   The solution was to get the last value returned by the 
            //   Graphics.DpiY property and
            //   divide by 72, since point is 1/72 of an inch, 
            //   no matter on what device we draw.
            //
            //   The source of this solution can be seen on CodeProject's article
            //   'OSD window with animation effect' - 
            //   http://www.codeproject.com/csharp/OSDwindow.asp

            //float fontSize = e.Graphics.DpiY * this.Font.SizeInPoints / 72;

            //this.drawPath.Reset();
            //this.drawPath.AddString(this.Text, this.Font.FontFamily,
            //                (int)this.Font.Style, fontSize,
            //                    point, StringFormat.GenericTypographic);

            // And finally, using our pen, all we have to do now
            //  is draw our graphics path to the screen. Voila!
            //e.Graphics.FillPath(this.forecolorBrush, this.drawPath);
            //e.Graphics.DrawPath(this.drawPen, this.drawPath);


            if (!this.Text.IsNullOrEmpty())
            {
                e.Graphics.DrawString(this.Text, this.Font, this.forecolorBrush, point);
            }

            //this.Round(this.Region);
           
            //e.Graphics.FillRectangle(new SolidBrush(this.BackColor),new Rectangle(0,0,this.Width,this.Height) );

            if (borderStyle== BorderStyle.FixedSingle)
            {
                //e.Graphics.draw

                var penWidth =drawBorderPen.Width;

                float width = this.Width-1;
                float height = this.Height-1;

                if (AutoSize)
                {
                    width = this.drawSize.Width +3- penWidth;
                    height = this.drawSize.Height+1 - penWidth;
                }


                var path = getPath(new RectangleF(penWidth, penWidth, width, height),
                                   _Radius);

                e.Graphics.DrawPath(drawBorderPen,path);

                //e.Graphics.DrawRectangle(drawBorderPen,new Rectangle(0,0,this.Width,this.Height));

                //ControlPaint.DrawBorder(e.Graphics,new Rectangle(0,0,this.Width,this.Height),borderColor,ButtonBorderStyle.Solid );
            }
            else if(borderStyle== BorderStyle.Fixed3D)
            {
                ControlPaint.DrawBorder3D(e.Graphics, new Rectangle(0, 0, this.Width+1, this.Height+1));
            }

        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this.forecolorBrush != null)
                    this.forecolorBrush.Dispose();

                if (this.drawPath != null)
                    this.drawPath.Dispose();

                if (this.drawPen != null)
                    this.drawPen.Dispose();

                if (drawBorderPen!=null)
                {
                    drawBorderPen.Dispose();
                }
            }
            base.Dispose(disposing);
        }
    }
}
